package com.handinfo.engine.resource;

import javax.microedition.rms.*;

import com.handinfo.util.Tools;

import java.io.*;

/**
 * @author Xia Mingwei
 * @version 1.0
 */
public final class RMSSerialize extends ResourceSerialize
{

	public RMSSerialize()
	{
	}

	public boolean openSerialize(String name, boolean needCreate,
			boolean onlyOpenCatalog)
	{
		m_name = name;
		m_needCreate = needCreate;
		try {
			if (onlyOpenCatalog) {
				openStoreCatalog();
			}
			else {
				openStoreCatalog();
				openStoreResource();
			}
		}
		catch (Exception ex) {
			Tools.debug(ex);
			try {
				Tools.debugPrintln("资源RMS异常,清除资源RMS!");
				RecordStore.deleteRecordStore(ResourceManager.__STORE_NAME + "_resource");
				RecordStore.deleteRecordStore(ResourceManager.__STORE_NAME + "_catalog");
			}
			catch (Exception e) {
				Tools.debug(e);
				Tools.debugPrintln("清除资源RMS异常!");
			}
			return false;
		}
		return true;
	}

	public boolean closeSerialize()
	{
		try {
			closeStoreCatalog();
			closeStoreResource();
		}
		catch (Exception ex) {
			Tools.debug(ex);
			return false;
		}
		return true;
	}

	public byte[] readResource(ResHead head)
	{
		byte[] data = null;
		if (exist(head)) {
			data = new byte[m_resourceDataLength];
			readStoreResource(data, m_resourceRecordIndex, m_resourceRecordNum);
			updateLastUsedFlag();
		}
		return data;
	}

	public void writeResource(ResHead head, byte[] data)
	{
		int[] ret;
		if (exist(head)) {
			ret = writeStoreResource(m_resourceRecordIndex, data);
			writeStoreCatalog(m_catalogRecordIndex, head, data.length, ret[0],
					ret[1]);
		}
		else {
//			System.out.println("RMS head.url ="+head.m_url);
			boolean can = canPushResource(data.length);
			int recordId;
			if (can) {
				ret = writeStoreResource(-1, data);
				writeStoreCatalog(-1, head, data.length, ret[0], ret[1]);
			}
			else {
				recordId = findCanReplaceResourceIndex(data.length);
				byte[] item = readCatalogRecord(recordId);
				parseCatalogRecord(item);
				ret = writeStoreResource(m_resourceRecordIndex, data);
				writeStoreCatalog(m_catalogRecordIndex, head, data.length,
						ret[0], ret[1]);
			}
		}
	}

	public boolean exist(ResHead head)
	{
		try {
			RecordEnumeration enumeration = m_storeCatalog.enumerateRecords(
					null, null, false);
			short index = 0;
			while (enumeration.hasNextElement()) {
				byte[] data = (byte[]) enumeration.nextRecord();
				parseCatalogRecord(data);
				index++;
				if (m_type != head.m_type || m_url.compareTo(head.m_url) != 0
						|| m_version != head.m_version)
					continue;
				m_catalogRecordIndex = index;
				return true;
			}
			enumeration.destroy();
		}
		catch (Exception ex) {
			Tools.debug(ex);
		}
		return false;
	}

	public void updateLastUsedFlag(ResHead head)
	{
		if (!exist(head))
			return;

		try {
			byte[] data = readCatalogRecord(m_catalogRecordIndex);
			parseCatalogRecord(data);
			ByteArrayOutputStream bao = new ByteArrayOutputStream();
			DataOutputStream dos = new DataOutputStream(bao);
			dos.writeByte(m_type);
			dos.writeUTF(m_url);
			dos.writeInt(m_version);
			dos.writeLong(System.currentTimeMillis());
			dos.writeInt(m_resourceDataLength);
			dos.writeShort(m_resourceRecordIndex);
			dos.writeByte(m_resourceRecordNum);

			data = null;
			data = bao.toByteArray();
			m_storeCatalog
					.setRecord(m_catalogRecordIndex, data, 0, data.length);

		}
		catch (Exception ex) {
			Tools.debug(ex);
		}
	}
	
	public void updateLastUsedFlag()
	{
		try {
			byte[] data = readCatalogRecord(m_catalogRecordIndex);
			parseCatalogRecord(data);
			ByteArrayOutputStream bao = new ByteArrayOutputStream();
			DataOutputStream dos = new DataOutputStream(bao);
			dos.writeByte(m_type);
			dos.writeUTF(m_url);
			dos.writeInt(m_version);
			dos.writeLong(System.currentTimeMillis());
			dos.writeInt(m_resourceDataLength);
			dos.writeShort(m_resourceRecordIndex);
			dos.writeByte(m_resourceRecordNum);

			data = null;
			data = bao.toByteArray();
			m_storeCatalog.setRecord(m_catalogRecordIndex, data, 0, data.length);
		}
		catch (Exception ex) {
			Tools.debug(ex);
		}
	}

	protected void openStoreCatalog() throws Exception
	{
		StringBuffer catalog = new StringBuffer(m_name);
		catalog.append("_catalog");
		try {
			m_storeCatalog = RecordStore.openRecordStore(catalog.toString(),
					false);
		}
		catch (Exception e) {
			try {
				m_storeCatalog = RecordStore.openRecordStore(catalog.toString(),
						true);
			}
			catch (Exception ex) {
				Tools.debug(ex);
				throw new Exception();
			}
		}
	}

	protected void closeStoreCatalog() throws Exception
	{
		if (m_storeCatalog != null) {
			m_storeCatalog.closeRecordStore();
			m_storeCatalog = null;
		}
	}

	protected void openStoreResource() throws Exception
	{
		StringBuffer resource = new StringBuffer(m_name);
		resource.append("_resource");
		m_storeResource = RecordStore.openRecordStore(resource.toString(),
				m_needCreate);
	}

	protected void closeStoreResource() throws Exception
	{
		if (m_storeResource != null) {
			m_storeResource.closeRecordStore();
			m_storeResource = null;
		}
	}

	protected void parseCatalogRecord(byte[] data)
	{
		ByteArrayInputStream bai = new ByteArrayInputStream(data);
		DataInputStream dis = new DataInputStream(bai);
		try {
			m_type = dis.readByte();
			m_url = dis.readUTF();
			m_version = dis.readInt();
			m_lastUsedTime = dis.readLong();
			m_resourceDataLength = dis.readInt();
			m_resourceRecordIndex = dis.readShort();
			m_resourceRecordNum = dis.readByte();
		}
		catch (Exception ex) {
			Tools.debug(ex);
		}
	}

	protected byte[] readCatalogRecord(int recordId)
	{
		byte[] data = null;
		try {
			data = m_storeCatalog.getRecord(recordId);
		}
		catch (Exception ex) {
			Tools.debug(ex);
		}
		return data;
	}

	protected int readStoreResource(byte[] buffer, int resourceRecordIndex,
			int resourceRecordNum)
	{
		int offset = 0;
		int length;
		try {
			for (int j = 0; j < resourceRecordNum; j++) {
				length = m_storeResource.getRecord(resourceRecordIndex + j,
						buffer, offset);
				offset += length;
			}
		}
		catch (Exception ex) {
			Tools.debug(ex);
		}
		return offset;
	}

	protected void writeStoreCatalog(int catalogRecordIndex, ResHead head,
			int resourceDataLength, int resourceRecordIndex,
			int resourceRecordNum)
	{
		ByteArrayOutputStream bao = new ByteArrayOutputStream();
		DataOutputStream dos = new DataOutputStream(bao);
		try {
			dos.writeByte(head.m_type);
			dos.writeUTF(head.m_url);
			dos.writeInt(head.m_version);
			dos.writeLong(System.currentTimeMillis());
			dos.writeInt(resourceDataLength);
			dos.writeShort(resourceRecordIndex);
			dos.writeByte(resourceRecordNum);
			byte[] data = bao.toByteArray();
			if (catalogRecordIndex < 0) {
				m_storeCatalog.addRecord(data, 0, data.length);
			}
			else {
				m_storeCatalog.setRecord(catalogRecordIndex, data, 0,
						data.length);
			}
		}
		catch (Exception ex) {
			Tools.debug(ex);
		}
	}

	protected int[] writeStoreResource(int resourceRecordIndex, byte[] data)
	{
		int lines = (data.length + m_maxSingleCapacity - 1)
				/ m_maxSingleCapacity;
		int sgmax = data.length > m_maxSingleCapacity ? m_maxSingleCapacity
				: data.length;
		int offset = 0;
		int index = 0;
		try {
			if (resourceRecordIndex < 0) {
				for (int j = 0; j < lines; j++) {
					index = m_storeResource.addRecord(data, offset, sgmax);
					if (resourceRecordIndex < 0) {
						resourceRecordIndex = index;
					}
					offset += sgmax;
					if (data.length - offset < sgmax) {
						sgmax = data.length - offset;
					}
				}
			}
			else {
				for (int j = 0; j < lines; j++) {
					m_storeResource.setRecord(resourceRecordIndex + j, data,
							offset, sgmax);
					offset += sgmax;
					if (data.length - offset < sgmax) {
						sgmax = data.length - offset;
					}
				}
			}
		}
		catch (Exception ex) {
			Tools.debug(ex);
			return null;
		}
		int[] ret = { resourceRecordIndex, lines };
		return ret;
	}

	protected boolean canPushResource(int capacity)
	{
		try {
			RecordEnumeration enumeration = m_storeCatalog.enumerateRecords(
					null, null, false);
			int sum = 0;
			while (enumeration.hasNextElement()) {
				byte[] data = (byte[]) enumeration.nextRecord();
				parseCatalogRecord(data);
				sum += m_resourceDataLength;
			}
			sum += capacity;
			enumeration.destroy();
			return (sum < m_maxCapacity);
		}
		catch (Exception ex) {
			Tools.debug(ex);
			return false;
		}
	}

	protected int findCanReplaceResourceIndex(int capacity)
	{
		int id = 1;
		int pos = 0;
		try {
			RecordEnumeration enumeration = m_storeCatalog.enumerateRecords(
					null, null, false);
			int[][] sort = new int[enumeration.numRecords()][];
			long lastTime = System.currentTimeMillis();
			int index = 1;
			while (enumeration.hasNextElement()) {
				byte[] data = (byte[]) enumeration.nextRecord();
				parseCatalogRecord(data);
				if (m_lastUsedTime < lastTime) {
					lastTime = m_lastUsedTime;
					sort[pos] = new int[2];
					sort[pos][0] = index;
					sort[pos][1] = m_resourceDataLength;
					pos++;
				}
				index++;
			}
			for (int j = pos - 1; j >= 0; j--) {
				if (sort[j][1] >= capacity || capacity < m_maxSingleCapacity) {
					id = sort[j][0];
					break;
				}
			}
			enumeration.destroy();
		}
		catch (Exception ex) {
			Tools.debug(ex);
		}
		return id;
	}
	
	public void clear()
	{
		try {
			RecordStore.deleteRecordStore(ResourceManager.__STORE_NAME + "_resource");
			RecordStore.deleteRecordStore(ResourceManager.__STORE_NAME + "_catalog");
		}
		catch (Exception e) {
			Tools.debug(e);
		}
	}

	/**
	 * m_storeCatalog 代表存储目录，根据存储目录可以快速检索到存储对象属性信息 每条记录的格式为:
	 * type、url、version、lastUsedTime、dataLength、resRecordIndex、resRecordNum
	 */
	protected RecordStore m_storeCatalog; // 代表存储对象目录RMS
	protected RecordStore m_storeResource; // 代表存储对象数据集合RMS
	protected String m_name;
	protected boolean m_needCreate;

	protected short m_catalogRecordIndex;

	protected byte m_type;
	protected String m_url;
	protected int m_version;
	protected long m_lastUsedTime;
	protected int m_resourceDataLength;
	protected short m_resourceRecordIndex;
	protected byte m_resourceRecordNum;

}
